package com.geek.gkpush.framework.retry.model;

import com.geek.gkpush.framework.retry.enums.ExecStatusEnum;
import com.google.common.base.Objects;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

import java.io.Serializable;
import java.util.Date;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;

/**
 * <h1>重试延迟队列中的元数据模型对象。</h1><br/>
 *
 * <h1><br/><a href="https://u.geekbang.org/subject/arch3/100614201">Java 业务架构实战营：https://u.geekbang.org/subject/arch3/100614201</a></h1><br/><h1><a href="https://gitee.com/ylimhhmily/BizArchPracticeCamp">Java 业务架构实战营代码地址：https://gitee.com/ylimhhmily/BizArchPracticeCamp</a></h1><br/><h1><a href="https://u.geekbang.org/subject/java4th/1001148?source=app_share">高级 Java 工程体系课 2023 版：https://u.geekbang.org/subject/java4th/1001148?source=app_share</a></h1><br/><h1><a href="https://time.geekbang.org/column/intro/100312101?tab=catalog">Dubbo 源码剖析与实战：https://time.geekbang.org/column/intro/100312101?tab=catalog</a></h1>
 *
 * @author hmilyylimh
 * ^_^
 * @version 0.0.1
 * ^_^
 * @date 2023/10/3 10:14
 */
@Setter
@Getter
@ToString(callSuper = true)
public class RetryDelayModel implements Delayed, Serializable {

    private static final long serialVersionUID = 2513256763732497567L;

    /** <h2>分布式ID</h2> **/
    private String id;
    /** <h2>业务ID，bizId 用于串联一堆任务用的，没有业务逻辑，只是给查询数据库归类看的</h2> **/
    private String bizId;

    /** <h2>业务ID，唯一性：bizValue + bizType</h2> **/
    private String bizValue;
    /** <h2>业务类型.通过 bizTypeClass 需要计算出 bizType，而 bizType 以后就是 bean 的名称</h2> **/
    private String bizType;

    /** <h2>当前已执行的任务次数</h2> **/
    private Integer execTimes;
    /** <h2>任务的最大重试次数</h2> **/
    private Integer maxRetryTimes;
    /** <h2>当前已执行的任务的结果状态，参考枚举类：{@linkplain com.geek.gkpush.framework.retry.enums.ExecStatusEnum}</h2> **/
    private String execStatus;
    /** <h2>用于触发需要准备执行重试任务的时间，该时间会用于大小比较，时间小者先执行，时间大者后执行。</h2> **/
    private Long execTimeMillis;

    /** <h2>任务提取出来时的时间，即任务真正执行之前的时间</h2> **/
    private Date execStartTime;

    @Override
    public long getDelay(TimeUnit unit) {
        return execTimeMillis - System.currentTimeMillis();
    }

    @Override
    public int compareTo(Delayed o) {
        RetryDelayModel req = (RetryDelayModel)o;
        return this.toUniqueKey().compareTo(req.toUniqueKey());
    }

    /**
     * <h2>得到当前对象的唯一标识。</h2>
     *
     * @param:
     * @return: java.lang.String
     */
    public String toUniqueKey() {
        return String.join("_", String.valueOf(execTimeMillis), getBizValue(), getBizType());
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        RetryDelayModel that = (RetryDelayModel) o;
        return Objects.equal(bizValue, that.bizValue) && Objects.equal(bizType, that.bizType);
    }

    @Override
    public int hashCode() {
        return Objects.hashCode(bizValue, bizType);
    }

    /**
     * <h2>拷贝一份新的数据。</h2>
     *
     * @param:
     * @return: com.geek.gkpush.framework.retry.model.RetryDelayModel
     */
    public RetryDelayModel copy() {
        RetryDelayModel newModel = new RetryDelayModel();

        newModel.setId(getId());
        newModel.setBizId(getBizId());

        newModel.setBizValue(getBizValue());
        newModel.setBizType(getBizType());

        newModel.setExecTimes(getExecTimes() + 1);
        newModel.setMaxRetryTimes(getMaxRetryTimes());
        newModel.setExecStatus(ExecStatusEnum.FAIL.getCode());

        return newModel;
    }

    /**
     * <h2>是否超过最大执行次数了。</h2>
     *
     * @param:
     * @return: <h2>{@code true}：超过最大执行次数了，{@code false}：没有超过最大执行次数。</h2>
     */
    public boolean overTimesFlag() {
        return getExecTimes() >= getMaxRetryTimes();
    }
}